package org.vison.wonfu.framework.soa.governance.circuitBreaker;

import org.vison.wonfu.framework.soa.SoaConstants;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class CircuitBreakerManager {
    private static final ConcurrentHashMap<String, CircuitBreaker> breakers = new ConcurrentHashMap<>();

    /**
     * key 必须是唯一的，通常使用接口名+方法名的组合作为key。
     * 优先使用CommandKey
     * 其次: appid@servicePath+method
     * @param key
     * @return
     */
    public static CircuitBreaker getOrCreate(String key) {
        return breakers.computeIfAbsent(key, k -> new CircuitBreaker());
    }

    public static void put(String key, CircuitBreaker breaker) {
        breakers.putIfAbsent(key, breaker);
    }

    public static class CircuitBreaker {
        private enum State {CLOSED, OPEN, HALF_OPEN}

        private final AtomicLong requestCount = new AtomicLong(0);
        private final AtomicLong errorCount = new AtomicLong(0);
        private final AtomicLong lastResetTime = new AtomicLong(System.currentTimeMillis());
        private volatile State state = State.CLOSED;
        private final AtomicLong halfOpenStartTime = new AtomicLong(System.currentTimeMillis());;
        private final AtomicInteger consecutiveSuccesses = new AtomicInteger(0);

        /**
         * 主动降级开关
         */
        private volatile boolean manualBreaker = SoaConstants.DEFAULT_CIRCLE_BREAKER_MANUAL_BREAKER;

        public void setManualBreaker(boolean manualBreaker) {
            this.manualBreaker = manualBreaker;
        }

        /**
         * 熔断是否启用
         */
        private volatile boolean enable = SoaConstants.DEFAULT_CIRCLE_BREAKER_ENABLE;

        public void setEnable(boolean enable) {
            this.enable = enable;
        }

        /**
         * 滑动窗口大小，单位为秒。默认为10s，即每10s重置一次计数器。
         */
        private int slidingWindowSize = SoaConstants.DEFAULT_CIRCLE_BREAKER_SLIDING_WINDOW_SIZE;
        /**
         * 半开间隔，单位为秒。默认为5s，即从打开到半开的状态转换时间为5s。
         */
        private int halfOpenInterval = SoaConstants.DEFAULT_CIRCLE_BREAKER_HALF_OPEN_INTERVAL;

        /**
         * 半开状态下连续成功次数阈值，默认为3次。当在半开状态下的请求连续成功达到此阈值时，将重新设置为关闭状态。
         */
        private int successThreshold = SoaConstants.DEFAULT_CIRCLE_BREAKER_SUCCESS_THRESHOLD;


        /**
         * 请求阈值，默认为100次。当在一定时间内（滑动窗口大小）的请求次数超过此阈值时，将触发断路器打开或半开状态的检测转换逻辑。
         */
        private int requestThreshold = SoaConstants.DEFAULT_CIRCLE_BREAKER_REQUEST_THRESHOLD;

        /**
         * 错误率阈值，默认为0.5（即50%）。当在一定时间内请求次数及失败比例超过此阈值时，将触发断路器打开
         */
        private double errorRateThreshold = SoaConstants.DEFAULT_CIRCLE_BREAKER_ERROR_RATE_THRESHOLD;

        public void setSlidingWindowSize(int slidingWindowSize) {
            this.slidingWindowSize = slidingWindowSize;
        }
        public void setHalfOpenInterval(int halfOpenInterval) {
            this.halfOpenInterval = halfOpenInterval;
        }

        public void setSuccessThreshold(int successThreshold) {
            this.successThreshold = successThreshold;
        }

        public void setRequestThreshold(int requestThreshold) {
            this.requestThreshold = requestThreshold;
        }

        public void setErrorRateThreshold(double errorRateThreshold) {
            this.errorRateThreshold = errorRateThreshold;
        }

        public synchronized boolean allowRequest() {
            if(manualBreaker){
                return false;
            }
            if(!enable){
                return true;
            }
            long now = System.currentTimeMillis();
            long elapsed = now - lastResetTime.get();
            long slidingWindowSizeMs = slidingWindowSize * 1000L;
            long halfOpenIntervalMs = halfOpenInterval * 1000L;
            switch (state) {
                case CLOSED:
                    if (elapsed > slidingWindowSizeMs) {
                        resetMetrics();
                    }
                    return true;
                case OPEN:
                    if (now - halfOpenStartTime.get() > halfOpenIntervalMs) { // 半开间隔
                        state = State.HALF_OPEN;
                        resetHalfOpenMetrics();
                        return true;
                    }
                    return false;
                case HALF_OPEN:
                    return true;
                default:
                    return false;
            }
        }

        public synchronized void recordSuccess() {
            if (state == State.HALF_OPEN) {
                if (consecutiveSuccesses.incrementAndGet() >= successThreshold) {
                    state = State.CLOSED;
                }
            }
            requestCount.incrementAndGet();
        }

        public synchronized void recordFailure() {
            errorCount.incrementAndGet();
            requestCount.incrementAndGet();

            if (state != State.OPEN) {
                double errorRate = (double) errorCount.get() / requestCount.get();
                if (requestCount.get() > requestThreshold && errorRate > errorRateThreshold) {
                    state = State.OPEN;
                    resetHalfOpenMetrics();
                }
            }
            // 半开状态下，如果有一次失败，则重置连续成功计数器
            if (state == State.HALF_OPEN) {
                resetHalfOpenMetrics();
                state = State.OPEN;
            }
        }

        /**
         * 重置全开计数器
         */
        private void resetMetrics() {
            requestCount.set(0);
            errorCount.set(0);
            lastResetTime.set(System.currentTimeMillis());
        }

        /**
         * 重置半开计数器
         */
        private void resetHalfOpenMetrics() {
            consecutiveSuccesses.set(0);
            halfOpenStartTime.set(System.currentTimeMillis());
        }

    }
}